
#include <Windows.h>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\nonfree\nonfree.hpp>
#include <opencv2\nonfree\features2d.hpp>
#include <opencv2\features2d\features2d.hpp>
#include <iostream>
#include <myOpenCVLIB\myheader.h>

using namespace cv;
using namespace std;

#ifndef WEBCAM_ADDRESS
#define WEBCAM_ADDRESS "http://192.168.1.13:8080/video?x.mjpeg"
#endif

#ifndef MATCH_IMAGE
#define MATCH_IMAGE IMG_VIC4
#endif

const string srcWndName			= "IPCAM";
const string harrisWndName		= "HarrisCornerDetector";
const string shiWndName			= "Shi-Tomasi detector";
const string surfWndName		= "SURF feature detector";
const string siftWndName		= "SIFT feature detector";


string detectorWdnName = "";

const int NOTHING = 0
	, HARRIS_DETECTOR			= 1
	, SHI_TOMASI_DETECTOR		= 2
	, SURF_FEATURE_DETECTOR		= 3
	, SIFT_FEATURE_DETECTOR		= 4;
int detector			= 0;
const int max_detector	= 4;

Mat frame, frameGray, dst, dstNorm, dstNormScaled;


//HARRIS PARAMETERS
const int maxThresh			= 255;
int thresh					= 250;
const int maxBlockSize		= 255;
int blockSize				= 2;
const int maxApertureSize	= 255;
int apertureSize			= 3;
const int maxK				= 100;
int  k						= 4;
	
//SHI-TOMASI PARAMETERS
int cornersNum				= 23;
const int maxCornersNum		= 100;

double qualityLevel			= 0.01;
double minDistance			= 10;
int shi_blockSize			= 3;
bool useHarrisDetector		= false;
double shi_k				= 0.04;
Mat frameCopy;
vector<Point2f> corners;

//SURF FEATURE DETECTOR PARAMS
const int maxHessianThresh = 1000;
int hessianThresh = 400;
vector <KeyPoint> keyPoints;
SurfFeatureDetector surfDetector(hessianThresh);

//SURF FEATURE DESCRIPTION PARAMS
bool initialized = false;
bool surfDescriptorON = false;
Mat matchImage, matchDescriptors, frameDescriptors;
SurfDescriptorExtractor surfExtractor ;
vector <KeyPoint> matchKeyPoints;
vector< DMatch > matches;
BFMatcher bfMatcher = BFMatcher(NORM_L2);


//SIFT FEATURE DETECTOR PARAMS
SiftFeatureDetector siftDetector = SiftFeatureDetector();

//FUNCTION HEADERS

void SIFTFeaureDetect();
void SURFFeaturesDetect();
void cornerHarris_detect();
void shi_tomasi_detect();
void onChange(int ,void*){}
void changeDetector(int, void*);
double getDiv100(int param){return (double) param / 100; }
void hessianChange(int, void*){surfDetector.hessianThreshold = hessianThresh;}
void buttonCallback(int state, void * data){}


long __stdcall WindowProcedure( HWND window, unsigned int msg, WPARAM wp, LPARAM lp );
void createWindow();
void onComponentClick(HWND window, unsigned int msg, WPARAM wParam, LPARAM lp);

int main(){
	
	cout << "use optimized " << useOptimized() <<endl;

	createWindow();

	VideoCapture videoCap;
	
	string videoName(VID_CRASH);

	if(!videoCap.open(videoName)){
		cout << "cannot open video";
		getchar();
		return -1;
	}

	cout << "videocapture open OK\n";
		
	namedWindow(srcWndName); 
	
	createTrackbar("detector", srcWndName, &detector, max_detector, changeDetector);


	cout << "running...\n";
		
	while(videoCap.read(frame)){
		//imshow(srcWndName, frame);
		//cornerHarris_detect();

		switch(detector){
			case HARRIS_DETECTOR:
				cornerHarris_detect();
				break;
			case SHI_TOMASI_DETECTOR:
				shi_tomasi_detect();
				break;
			case SURF_FEATURE_DETECTOR:
				SURFFeaturesDetect();
				break;
			case SIFT_FEATURE_DETECTOR:
				SIFTFeaureDetect();
				break;
			default:
				imshow(srcWndName, frame);
				break;
		}

		if(waitKey(1) == 13){break;}
	}
	videoCap.release();
	cout << "video relased\n";
	

	waitKey();
	return 0;
}

void SIFTFeaureDetect(){
	cvtColor(frame, frameGray, CV_BGR2GRAY );

	siftDetector.detect(frameGray,keyPoints);
	
	drawKeypoints(frameGray, keyPoints, frame, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
	imshow(srcWndName, frame);
}

void SURFFeaturesDetect(){

		double t1 = (double) getTickCount ();
	cvtColor(frame, frameGray, CV_BGR2GRAY );
		t1 = ((double)getTickCount() - t1) / getTickFrequency();
		cout << "t1 " << t1 <<endl;

		t1 = (double) getTickCount ();

	surfDetector.detect(frameGray, keyPoints);
		
		t1 = ((double)getTickCount() - t1) / getTickFrequency();
		cout << "t2 " << t1 <<endl;

		

	if(surfDescriptorON){
			t1 = (double) getTickCount ();

		surfExtractor.compute(frameGray, keyPoints, frameDescriptors);
		bfMatcher.match(matchDescriptors, frameDescriptors, matches);
		drawMatches(matchImage, matchKeyPoints, frameGray, keyPoints, matches, dst);

			t1 = ((double)getTickCount() - t1) / getTickFrequency();
			cout << "t3 " << t1 <<" "<< matches.size() <<endl;

		imshow(surfWndName, dst);
	}
	else{
			t1 = (double) getTickCount ();
		//drawKeypoints(frameGray, keyPoints, frame, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
		for(int i=0;i < keyPoints.size();i++){
			circle(frame,keyPoints[i].pt, 4, Scalar(0,255,0));
		}

		
			t1 = ((double)getTickCount() - t1) / getTickFrequency();
			cout << "t3 " << t1 <<" "<< keyPoints.size() <<endl;
		imshow(srcWndName, frame);
	}

	
}

void shi_tomasi_detect(){
	//frameCopy = Mat::zeros(frame.size(), CV_32FC1);
	if(cornersNum < 0){cornersNum = 1;}
	cvtColor(frame, frameGray, CV_BGR2GRAY );

	goodFeaturesToTrack(frameGray
		, corners
		, cornersNum
		, qualityLevel
		, minDistance
		, Mat()
		, shi_blockSize
		, useHarrisDetector
		, shi_k);

		double t1 = (double) getTickCount ();

	for(int i=0;i< corners.size(); i++){
		circle(frame, corners[i], 4, Scalar(0,255,0)); 
	}
		t1 = ((double)getTickCount() - t1) / getTickFrequency();
		cout << "t3 " << t1 <<" "<< corners.size() <<endl;

	imshow(srcWndName, frame);
		
}

void cornerHarris_detect(){
	
	dst = Mat::zeros(frame.size(), CV_32FC1);
	
	cvtColor(frame, frameGray, CV_BGR2GRAY );
	cornerHarris(frameGray, dst, blockSize, apertureSize, getDiv100(k), BORDER_DEFAULT);
	normalize(dst, dstNorm, 0, 255, NORM_MINMAX, CV_32FC1, Mat());
	convertScaleAbs(dstNorm, dstNormScaled);
		int ifs =0;
		double t1 = (double) getTickCount ();
		
	for(int row=0; row < dstNorm.rows; row++){
		for(int col=0; col < dstNorm.cols; col++){
			if( (int) dstNorm.at<float>(row, col) > thresh ){
				if(ifs++ > 1500){row = dstNorm.rows; col = dstNorm.cols; break;}

				//rectangle(frame,Point(col-5,row-5),Point(col+5,row+5), Scalar(0,255,0));
				circle(frame, Point(col, row), 5, Scalar(0,255,0), 2, 8, 0);
			}
		}
	}
		t1 = ((double)getTickCount() - t1) / getTickFrequency();
		cout << "t3 " << t1 <<" "<< ifs <<endl;
	imshow(srcWndName, frame);
		
	//imshow(harrisWndName,dstNormScaled);
}

void changeDetector(int, void*){
	if(detectorWdnName != ""){
		destroyWindow(detectorWdnName);
	}
	switch(detector){
			case HARRIS_DETECTOR:
				detectorWdnName = harrisWndName;
				namedWindow(detectorWdnName);
				createTrackbar("threshold",		detectorWdnName,	&thresh,		maxThresh,		onChange );
				createTrackbar("block size",	detectorWdnName,	&blockSize,		maxBlockSize,	onChange );
				createTrackbar("aperture",		detectorWdnName,	&apertureSize,	maxApertureSize,onChange );
				createTrackbar("k",				detectorWdnName,	&k,				maxK,			onChange );
				break;

			case SHI_TOMASI_DETECTOR:
				detectorWdnName = shiWndName;
				namedWindow(detectorWdnName);
				createTrackbar("corners num",	detectorWdnName,	&cornersNum,	maxCornersNum,	onChange );
				break;
			case SURF_FEATURE_DETECTOR:
				detectorWdnName = surfWndName;
				namedWindow(detectorWdnName);
				createTrackbar("hessian thresh",detectorWdnName,	&hessianThresh,	maxHessianThresh,hessianChange );
				
				break;

			case SIFT_FEATURE_DETECTOR:
				detectorWdnName = siftWndName;
				namedWindow(detectorWdnName);
				
				break;

			default:
				break;
		}
}


//WINDOWS API ------------------------------------------------------------------------------------------

#define qBUTTON_1		123
#define qCHECKBOX_1		22
#define qGROUP_BOX		2234
#define qGROUP_BOX_1	22341
#define qGROUP_BOX_2	22342
#define qGROUP_BOX_3	22343

void onComponentClick(HWND window, unsigned int msg, WPARAM wParam, LPARAM lp){
	switch(wParam){
		case qBUTTON_1:
			if(surfDescriptorON){
				surfDetector.detect(matchImage, matchKeyPoints);
			}
			break;

		case qCHECKBOX_1:

			if((BOOL)IsDlgButtonChecked(window, wParam)){
				CheckDlgButton(window, qCHECKBOX_1,BST_UNCHECKED);
				surfDescriptorON = false;
				//imshow(surfWndName, frame);
			}
			else{
				if(!initialized){
					matchImage = imread(MATCH_IMAGE, CV_LOAD_IMAGE_GRAYSCALE);
					if(!matchImage.data){cout << "cannot load match image!" << endl; break;}
					surfDetector.detect(matchImage, matchKeyPoints);
					surfExtractor.compute(matchImage, matchKeyPoints, matchDescriptors);
					initialized = true;
				}
				CheckDlgButton(window, qCHECKBOX_1,BST_CHECKED);
				surfDescriptorON = true;
			}

			break;
	}
}

long __stdcall WindowProcedure( HWND window, unsigned int msg, WPARAM wParam, LPARAM lp ){
    switch(msg){

		case WM_COMMAND:
			onComponentClick( window, msg, wParam, lp);
			return 0l;

		case WM_CREATE:

			CreateWindowEx(NULL, "BUTTON", "Recrete matchKeypoints", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON
				, 20
				, 40
				, 200
				, 24
				, window
				, (HMENU) qBUTTON_1
				, GetModuleHandle(NULL)
				, NULL
			);
			
			CreateWindowEx(NULL, "BUTTON", "SURF IMAGE MATCH",  WS_VISIBLE | WS_CHILD | BS_CHECKBOX
				, 20
				, 20
				, 230
				, 24
				, window
				, (HMENU) qCHECKBOX_1
				, GetModuleHandle(NULL)
				, NULL
			);

			CreateWindowEx(NULL, "BUTTON", "Match type",  WS_VISIBLE | WS_CHILD | BS_GROUPBOX
				, 20	, 70
				, 230	, 100
				, window
				, (HMENU) qGROUP_BOX
				, GetModuleHandle(NULL)
				, NULL
			);
			CreateWindowEx(NULL, "BUTTON", "none",  WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON | WS_GROUP
				, 25	, 90
				, 200	, 20
				, window
				, (HMENU) qGROUP_BOX_1
				, GetModuleHandle(NULL)
				, NULL
			);
			CreateWindowEx(NULL, "BUTTON", "SURF BF match",  WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON 
				, 25	, 110
				, 200	, 20
				, window
				, (HMENU) qGROUP_BOX_2
				, GetModuleHandle(NULL)
				, NULL
			);
			CreateWindowEx(NULL, "BUTTON", "SURF FLANN match",  WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON 
				, 25	, 130
				, 200	, 20
				, window
				, (HMENU) qGROUP_BOX_3
				, GetModuleHandle(NULL)
				, NULL
			);
			

			return 0l;

        case WM_DESTROY:
            //std::cout << "\ndestroying window\n" ;
            PostQuitMessage(0) ;
            return 0L ;
        case WM_LBUTTONDOWN:
            /*std::cout << "\nmouse left button down at (" << LOWORD(lp)
                      << ',' << HIWORD(lp) << ")\n" ;*/
            // fall thru
        default:
            //std::cout << '.' ;
			return DefWindowProc( window, msg, wParam, lp ) ;
    }
}

void createWindow(){
	const char *  myclass = "ADDITIONAL_WND" ;
	WNDCLASSEX wndclass = { sizeof(WNDCLASSEX)
		, CS_DBLCLKS
		, WindowProcedure
		, 0
		, 0
		, GetModuleHandle(0)
		, LoadIcon(0,IDI_APPLICATION)
		, LoadCursor(0,IDC_ARROW)
		, HBRUSH(COLOR_WINDOW+1)
		, 0
		, myclass
		, LoadIcon(0,IDI_APPLICATION) 
	};

	

	if(RegisterClassEx(&wndclass)){
		
		HWND window = CreateWindowEx(0
			,  myclass
			, "title"
			, WS_OVERLAPPEDWINDOW
			, 0
			, 0
			, 400
			, 200
			, 0
			, 0
			, GetModuleHandle(0)
			, 0
		);

		if(window){
			ShowWindow(window, SW_SHOWDEFAULT);
			/*MSG msg;
			while( GetMessage( &msg, 0, 0, 0 ) ){DispatchMessage(&msg);}*/
		}
	}
}
